/*
 * @(#) Cryptographer.java 2010-9-3
 *
 * Copyright (c) 2010, Strong Technology. All Rights Reserved.
 * Strong Technology. CONFIDENTIAL
 */
package cn.strong.gweva.common.util.security;

import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * 加密处理
 * 
 * @author lilanfei
 * @version 1.0
 * @since 2010-9-3
 */
public class Cryptographer
{

	private static final String RANDOM_ALGORITHM = "SHA1PRNG";

	private static final String SECRET_KEY_ALGORITHM = "AES";

	private static final String PUBLIC_KEY_ALGORITHM = "RSA";

	private static final String TRANSFORMATION = Cryptographer.SECRET_KEY_ALGORITHM
			+ "/CBC/PKCS5Padding";

	private Cryptographer()
	{
	}

	public static SecretKey generateKey()
	{
		try
		{
			final KeyGenerator generator = KeyGenerator
					.getInstance(Cryptographer.SECRET_KEY_ALGORITHM);
			generator.init(128, SecureRandom.getInstance(Cryptographer.RANDOM_ALGORITHM));

			return generator.generateKey();
		}
		catch (final NoSuchAlgorithmException ex)
		{
			assert false;
			throw new IllegalStateException(ex);
		}
	}

	public static SecretKey generateFixedKey()
	{
		final String seedSeed = Cryptographer.class.getSimpleName()
				+ Cryptographer.RANDOM_ALGORITHM;
		final byte[] fixedKeySeed = new byte[16]; // AES-128
		long hashValue = 17;
		for (int i = 0; i < seedSeed.length(); i++)
		{
			hashValue = 31 * hashValue + seedSeed.charAt(i);
		}

		System.arraycopy(String.valueOf(hashValue).getBytes(), 0, fixedKeySeed, 0,
				fixedKeySeed.length);

		return new SecretKeySpec(fixedKeySeed, Cryptographer.SECRET_KEY_ALGORITHM);
	}

	public static byte[] encrypt(final byte[] plainText, final SecretKey key) throws IOException
	{
		assert plainText != null : "plainText{null}";
		assert key != null : "key{null}";

		try
		{
			final Cipher cipher = Cipher.getInstance(Cryptographer.TRANSFORMATION);
			cipher.init(Cipher.ENCRYPT_MODE, key);

			final byte[] vector = cipher.getIV();
			final byte[] output = cipher.doFinal(plainText);
			final byte[] cipherText = new byte[vector.length + output.length];
			System.arraycopy(vector, 0, cipherText, 0, vector.length);
			System.arraycopy(output, 0, cipherText, vector.length, output.length);

			return cipherText;
		}
		catch (final NoSuchAlgorithmException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final NoSuchPaddingException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final InvalidKeyException ex)
		{
			throw new IOException(ex.getMessage());
		}
		catch (final IllegalBlockSizeException ex)
		{
			throw new IOException(ex.getMessage());
		}
		catch (final BadPaddingException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
	}

	public static byte[] decrypt(final byte[] cipherText, final SecretKey key) throws IOException
	{
		assert cipherText != null : "cipherText{null}";
		assert key != null : "key{null}";

		try
		{
			final Cipher cipher = Cipher.getInstance(Cryptographer.TRANSFORMATION);
			final byte[] vector = new byte[cipher.getBlockSize()];
			System.arraycopy(cipherText, 0, vector, 0, vector.length);
			final byte[] input = new byte[cipherText.length - vector.length];
			System.arraycopy(cipherText, vector.length, input, 0, input.length);

			cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(vector));

			return cipher.doFinal(input);
		}
		catch (final NoSuchAlgorithmException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final NoSuchPaddingException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final InvalidKeyException ex)
		{
			throw new IOException(ex.getMessage());
		}
		catch (final InvalidAlgorithmParameterException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final IllegalBlockSizeException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final BadPaddingException ex)
		{
			throw new IOException(ex.getMessage());
		}
	}

	public static KeyPair generateKeyPair()
	{
		try
		{
			final KeyPairGenerator generator = KeyPairGenerator
					.getInstance(Cryptographer.PUBLIC_KEY_ALGORITHM);
			generator.initialize(1024, SecureRandom.getInstance(Cryptographer.RANDOM_ALGORITHM));

			return generator.generateKeyPair();
		}
		catch (final NoSuchAlgorithmException ex)
		{
			assert false;
			throw new IllegalStateException(ex);
		}
	}

	public static byte[] encrypt(final SecretKey plainText, final PublicKey key) throws IOException
	{
		assert plainText != null : "plainText{null}";
		assert key != null : "key{null}";

		try
		{
			final Cipher cipher = Cipher.getInstance(Cryptographer.PUBLIC_KEY_ALGORITHM);
			cipher.init(Cipher.WRAP_MODE, key);

			return cipher.wrap(plainText);
		}
		catch (final NoSuchAlgorithmException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final NoSuchPaddingException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final InvalidKeyException ex)
		{
			throw new IOException(ex.getMessage());
		}
		catch (final IllegalBlockSizeException ex)
		{
			throw new IOException(ex.getMessage());
		}
	}

	public static SecretKey decrypt(final byte[] cipherText, final PrivateKey key)
			throws IOException
	{
		assert cipherText != null : "cipherText{null}";
		assert key != null : "key{null}";

		try
		{
			final Cipher cipher = Cipher.getInstance(Cryptographer.PUBLIC_KEY_ALGORITHM);
			cipher.init(Cipher.UNWRAP_MODE, key);

			return (SecretKey)cipher.unwrap(cipherText, Cryptographer.SECRET_KEY_ALGORITHM,
					Cipher.SECRET_KEY);
		}
		catch (final NoSuchAlgorithmException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final NoSuchPaddingException ex)
		{
			assert false;
			throw new IOException(ex.getMessage());
		}
		catch (final InvalidKeyException ex)
		{
			throw new IOException(ex.getMessage());
		}
	}

	public static String toHexString(byte[] b)
	{
		StringBuffer hexString = new StringBuffer();
		String plainText;
		for (byte aB : b)
		{
			plainText = Integer.toHexString(0xFF & aB);
			if (plainText.length() < 2)
			{
				plainText = "0" + plainText;
			}
			hexString.append(plainText);
		}
		return hexString.toString();
	}

	public static byte[] hexStringToByte(String str)
	{
		char strs[] = str.toCharArray();
		byte bs[] = new byte[str.length() / 2];
		int index = 0;
		for (int i = 0; i < strs.length - 1; i = i + 2)
		{
			String temp = str.substring(i, i + 2);
			bs[index++] = (byte)Integer.parseInt(temp, 16);
		}
		return bs;
	}

	public static String encodeToMD5(String code) throws IOException
	{
		if (code == null)
		{
			return null;
		}
		try
		{
			MessageDigest messageDigest = MessageDigest.getInstance("MD5");
			messageDigest.update(code.getBytes());
			return toHexString(messageDigest.digest());
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new IOException(e.getMessage());
		}
	}

	public static void main(String[] args) throws Exception {
		System.out.println(Cryptographer.encodeToMD5("111"));
	}
}
